#include "stdafx.h"
#include "ImageSplitter.h"
#include "Utils.h"

#include <string.h>

#pragma warning(push)
#pragma warning(disable: 4996)
#include <boost/gil/extension/numeric/sampler.hpp>
#include <boost/gil/extension/numeric/resample.hpp>

const int cVerySmallPiece = 3;

CImageSplitter::CImageSplitter() : m_SourceImage(NULL)
{
	ZeroMemory(&m_NewSize, sizeof(SIZE) );
	ZeroMemory(&m_PieceSize, sizeof(SIZE) );
	m_SourceImage = new rgb8_image_t;
}

CImageSplitter::~CImageSplitter() 
{
	delete m_SourceImage;
}

void CImageSplitter::SetJPEGQuailty(int Quality)
{
	m_ImageIOHelper.SetJPEGQuality(Quality);
}

void CImageSplitter::SetOutputImageType(ImageType_t ImageType)
{
	m_ImageIOHelper.SetOutputImageType(ImageType);
}

void CImageSplitter::OpenImage(const tstring& PathToFile)
{
	if(m_SourceImage != NULL)
		Close();

	if( !FileExists(PathToFile) )
		throw EUnableToOpenImage(); 

	m_ImageIOHelper.LoadImage( PathToFile, m_SourceImage );

	m_NewSize.cy = m_SourceImage->height();
	m_NewSize.cx = m_SourceImage->width();
	if( (m_NewSize.cy <= 0) || (m_NewSize.cx <= 0) )
		throw EUnableToOpenImage(); // fuck such image
}

void CImageSplitter::Close()
{
	delete m_SourceImage;
	m_SourceImage = NULL;
	ZeroMemory(&m_NewSize, sizeof(SIZE) );
	ZeroMemory(&m_PieceSize, sizeof(SIZE) );
	m_SourceImage = new rgb8_image_t;
}

LONG CImageSplitter::GetImageHeightInPixels()
{
	_ASSERT( m_SourceImage != NULL);

	return m_NewSize.cy;
}

LONG CImageSplitter::GetImageWidthInPixels()
{
	_ASSERT( m_SourceImage != NULL);

	return m_NewSize.cx;
}

void CImageSplitter::ResizeImage(LONG NewWidth, LONG NewHeight)
{
	_ASSERT( m_SourceImage != NULL);
	_ASSERT( (NewWidth > 0 ) && (NewHeight > 0) );

	m_NewSize.cx = NewWidth;
	m_NewSize.cy = NewHeight;
}

#pragma warning(push)
#pragma warning(disable: 4244)
void CImageSplitter::ResizeImage(FLOAT Scale)
{
	_ASSERT( m_SourceImage != NULL);

	m_NewSize.cx *= Scale;
	m_NewSize.cy *= Scale;

	_ASSERT( (m_NewSize.cx > 0 ) && (m_NewSize.cy > 0) );
}
#pragma warning(pop)

void CImageSplitter::SplitImage(LONG PieceWidth, LONG PieceHeight)
{
	_ASSERT( m_SourceImage != NULL);
	_ASSERT( (m_NewSize.cx != 0) && (m_NewSize.cy != 0) );

	m_PieceSize.cx = (PieceWidth <= m_NewSize.cx)?PieceWidth:m_NewSize.cx;
	m_PieceSize.cy = (PieceHeight <= m_NewSize.cy)?PieceHeight:m_NewSize.cy;
}

bool CImageSplitter::CheckSizes()
{
	 return (m_PieceSize.cx < m_NewSize.cx) &&
			(m_PieceSize.cy < m_NewSize.cy);
}

int CImageSplitter::GetHorisontalPieceCount()
{
	if( m_PieceSize.cx == 0 )
		return 0;
	int nHorisontalPieces = (m_NewSize.cx / m_PieceSize.cx);
	if( (m_NewSize.cx - nHorisontalPieces*m_PieceSize.cx) > cVerySmallPiece )
		nHorisontalPieces++;
	return nHorisontalPieces;
}

int CImageSplitter::GetVerticalPieceCount()
{
	if( m_PieceSize.cy == 0 )
		return 0;
	int nVerticalPieces = (m_NewSize.cy / m_PieceSize.cy);
	if( (m_NewSize.cy - nVerticalPieces*m_PieceSize.cy) > cVerySmallPiece )
		nVerticalPieces++;
	return nVerticalPieces;
}

int CImageSplitter::GetPieceCount()
{
	_ASSERT( m_SourceImage != NULL);

	return GetHorisontalPieceCount()*GetVerticalPieceCount();
}

void CImageSplitter::GetPiece(int Index, rgb8_image_t& Piece)
{
	_ASSERT( m_SourceImage != NULL);
	_ASSERT( Index >= 0 );

	if(Index > 0 ) // Zero piece is the whole image
	{
		SIZE SourcePieceSize = {0};
		GetSourcePieceSize(Index, SourcePieceSize);

		int ZeroBasedIndex = Index - 1; // http://xkcd.com/163/
		int Row = (ZeroBasedIndex / GetHorisontalPieceCount() );
		int Col = (ZeroBasedIndex % GetHorisontalPieceCount() );

		SIZE FullPieceSize = {0};
		GetSourcePieceSize( 1, FullPieceSize); // top left corner

		if( (Piece.height() != SourcePieceSize.cy) ||
			(Piece.width() != SourcePieceSize.cx) )
		{
			
			rgb8_image_t SourcePiece( SourcePieceSize.cx, SourcePieceSize.cy);
			copy_pixels( 
				subimage_view(const_view(*m_SourceImage),
				Col*FullPieceSize.cx, Row*FullPieceSize.cy, 
				SourcePieceSize.cx, SourcePieceSize.cy),
				view(SourcePiece) );
			resize_view(const_view(SourcePiece), view(Piece), bilinear_sampler());
		}
		else
		{
			copy_pixels(
				subimage_view(const_view(*m_SourceImage),
				Col*FullPieceSize.cx, Row*FullPieceSize.cy, 
				SourcePieceSize.cx, SourcePieceSize.cy),
				view(Piece) );
		}
	}
	else
	{
		if( (Piece.height() != m_SourceImage->height()) ||
			(Piece.width() != m_SourceImage->width()) )
		{
			resize_view(const_view(*m_SourceImage), view(Piece), bilinear_sampler());
		}
		else
		{
			copy_pixels(const_view(*m_SourceImage), view(Piece) );
		}
	}
}

void CImageSplitter::GetPieceSize(int Index, SIZE& PieceSize)
{
	_ASSERT( (m_PieceSize.cx != 0) && (m_PieceSize.cy != 0) );
	_ASSERT( Index != 0 );

	if( (Index % GetHorisontalPieceCount()) != 0 )
	{
		PieceSize.cx = m_PieceSize.cx;
	}
	else
	{
		LONG EdgePieceWidth =  m_NewSize.cx - m_PieceSize.cx*GetHorisontalPieceCount();
		if( EdgePieceWidth >= 0 )
			PieceSize.cx = m_PieceSize.cx;
		else
			PieceSize.cx = m_PieceSize.cx + EdgePieceWidth; // EdgePieceWidth < 0 (!)
	}
	if( ((float)Index / (float)GetHorisontalPieceCount() ) <= (float)(GetVerticalPieceCount() - 1) )
	{
		PieceSize.cy = m_PieceSize.cy;
	}
	else
	{
		LONG EdgePieceHeight = m_NewSize.cy - m_PieceSize.cy*GetVerticalPieceCount();
		if( EdgePieceHeight >= 0 )
			PieceSize.cy = m_PieceSize.cy;
		else
			PieceSize.cy = m_PieceSize.cy + EdgePieceHeight; // EdgePieceHeight < 0 (!)
	}
}
	
void CImageSplitter::GetSourcePieceSize(int Index, SIZE& PieceSize)
{
	_ASSERT( Index >= 0 );
	_ASSERT( m_SourceImage != NULL );

	FLOAT HorisontalScale = (FLOAT)(m_SourceImage->width()) / (FLOAT)m_NewSize.cx;
	FLOAT VerticalScale =   (FLOAT)(m_SourceImage->height()) / (FLOAT)m_NewSize.cy;

	SIZE ResizedPieceSize = {0};
	GetPieceSize(Index, ResizedPieceSize);
	
#pragma warning(push)
#pragma warning(disable: 4244) // conversion from 'FLOAT' to 'LONG', possible loss of data
	PieceSize.cx = ResizedPieceSize.cx*HorisontalScale;
	PieceSize.cy = ResizedPieceSize.cy*VerticalScale;
#pragma warning(pop)
}

void CImageSplitter::GetPiece(int Index, LPCTSTR PathToFile)
{
	_ASSERT( m_SourceImage != NULL);
	_ASSERT( PathToFile != NULL);
	_ASSERT( Index >= 0 );

	if( Index > GetPieceCount() )
		throw EUnableToSaveImage();
	
	SIZE PieceSize = {0};
	if( Index != 0)
		GetPieceSize(Index, PieceSize);
	else
	{
		PieceSize.cx = GetImageWidthInPixels();
		PieceSize.cy = GetImageHeightInPixels();
	}
	rgb8_image_t Piece(PieceSize.cx, PieceSize.cy);
	GetPiece(Index, Piece);

	m_ImageIOHelper.SaveImage( PathToFile, Piece );
}

#pragma warning(pop)
